# library(clusterProfiler)
# pos_gene <- dplyr::filter(signf_associate, cor == "positive")$gene
# neg_gene <- dplyr::filter(signf_associate, cor == "negative")$gene
# GO_DATA <- get_GO_data(OrgDb = org.Hs.eg.db, ont = "ALL", keytype = "SYMBOL")
# pos_rich <- enrich_GO(gene = pos_gene, GO_DATA = GO_DATA, pval = 0.05)
# neg_rich <- enrich_GO(gene = neg_gene, GO_DATA = GO_DATA, pval = 0.05)
# all_rich <- enrich_GO(gene = signf_associate$gene, GO_DATA = GO_DATA, pval = 0.05)


library(DOSE)
library(GOSemSim)
library(clusterProfiler)
library(org.Hs.eg.db)
#
get_GO_data <- function(OrgDb, ont, keytype) {
    GO_Env <- get_GO_Env()
    use_cached <- FALSE

    ont2 <- NULL
    if (exists("ont", envir = GO_Env, inherits = FALSE))
        ont2 <- get("ont", envir = GO_Env)

    if (exists("organism", envir=GO_Env, inherits=FALSE) &&
        exists("keytype", envir=GO_Env, inherits=FALSE) &&
        !is.null(ont2)) {

        org <- get("organism", envir=GO_Env)
        kt <- get("keytype", envir=GO_Env)

        if (org == get_organism(OrgDb) &&
            keytype == kt &&
            (ont == ont2 || ont2 == "ALL") &&
            exists("goAnno", envir=GO_Env, inherits=FALSE)) {
            ## https://github.com/GuangchuangYu/clusterProfiler/issues/182
            ## && exists("GO2TERM", envir=GO_Env, inherits=FALSE)){

            use_cached <- TRUE
        }
    }

    if (use_cached) {
        goAnno <- get("goAnno", envir=GO_Env)            
        if (!is.null(ont2) && ont2 != ont) { ## ont2 == "ALL"
            goAnno <- goAnno[goAnno$ONTOLOGYALL == ont,]
        } 
    } else {
        OrgDb <- load_OrgDb(OrgDb)
        kt <- AnnotationDbi::keytypes(OrgDb)
        if (! keytype %in% kt) {
            stop("keytype is not supported...")
        }

        kk <- AnnotationDbi::keys(OrgDb, keytype=keytype)

        goterms <- AnnotationDbi::Ontology(GO.db::GOTERM)
        if (ont != "ALL") {
            goterms <- goterms[goterms == ont]
        }
        go2gene <- suppressMessages(
            AnnotationDbi::mapIds(OrgDb, keys=names(goterms), column=keytype,
                                  keytype="GOALL", multiVals='list')
        )
        goAnno <- stack(go2gene)
        colnames(goAnno) <- c(keytype, "GOALL")
        goAnno <- unique(goAnno[!is.na(goAnno[,1]), ])
        goAnno$ONTOLOGYALL <- goterms[goAnno$GOALL]

        assign("goAnno", goAnno, envir=GO_Env)
        assign("keytype", keytype, envir=GO_Env)
        assign("ont", ont, envir = GO_Env)
        assign("organism", get_organism(OrgDb), envir=GO_Env)
    }

    GO2GENE <- unique(goAnno[, c(2,1)])

    GO_DATA <- build_Anno(GO2GENE, get_GO2TERM_table())
    
    goOnt.df <- goAnno[, c("GOALL", "ONTOLOGYALL")] %>% unique

    if (!is.null(ont2) && ont2 == "ALL") {
        return(GO_DATA)
    }

    goOnt <- goOnt.df[,2]
    names(goOnt) <- goOnt.df[,1]
    assign("GO2ONT", goOnt, envir=GO_DATA)

    return(GO_DATA)
}

get_organism <- getFromNamespace("get_organism", "DOSE")
build_Anno <- getFromNamespace("build_Anno", "DOSE")

get_GO_Env <- function () {
    if (!exists(".GO_clusterProfiler_Env", envir = .GlobalEnv)) {
        pos <- 1
        envir <- as.environment(pos)
        assign(".GO_clusterProfiler_Env", new.env(), envir=envir)
    }
    get(".GO_clusterProfiler_Env", envir = .GlobalEnv)
}
 
get_GOTERM <- function() {
    pos <- 1
    envir <- as.environment(pos)
    if (!exists(".GOTERM_Env", envir=envir)) {
        assign(".GOTERM_Env", new.env(), envir)
    }
    GOTERM_Env <- get(".GOTERM_Env", envir = envir)
    if (exists("GOTERM.df", envir = GOTERM_Env)) {
        GOTERM.df <- get("GOTERM.df", envir=GOTERM_Env)
    } else {
        GOTERM.df <- AnnotationDbi::toTable(GO.db::GOTERM)
        assign("GOTERM.df", GOTERM.df, envir = GOTERM_Env)
    }
    return(GOTERM.df)
}

get_GO2TERM_table <- function() {
    GOTERM.df <- get_GOTERM()
    GOTERM.df[, c("go_id", "Term")] %>% unique
}

## fisher富集
# set1 所有背景
# set2 GO term
# set3 感兴趣基因
enrich_fisher <- function(set1, set2, set3) {
    a11 <- length(intersect(set2, set3))
    a12 <- length(setdiff(set2, set3))
    a21 <- length(setdiff(set3, set2))
    a22 <- length(setdiff(setdiff(set1, set2), set3))
    dat <- matrix(c(a11,a12,a21,a22),2,2,byrow=T)
    pvalue <- fisher.test(dat, alternative = "greater")$p.value
    GeneRatio <- paste(a11, "/", a21+a11, sep = "")
    BgRatio <- paste(length(set2), "/", length(set1), sep = "")
    geneID <- paste(intersect(set2, set3), collapse = "/")
    res <- data.frame(GeneRatio = GeneRatio, BgRatio = BgRatio, pvalue = pvalue, geneID = geneID)
    return(res)
}

enrich_GO <- function(gene, GO_DATA = GO_DATA, pval = 0.05) {
    terms <- names(GO_DATA$PATHID2EXTID)
    res <- purrr::map(terms, function(x) {
        enrich_fisher(set1 = names(GO_DATA$EXTID2PATHID),
                      set2 = GO_DATA$PATHID2EXTID[[x]],
                      set3 = gene)
    })
    names(res) <- terms
    descri <- GO_DATA$PATHID2NAME[terms]
    res <- purrr::pmap(list(res, terms, descri), function(x, y, z) {data.frame(ID = y, Description  = z, x)})
    res <- do.call(rbind, res)
    res <- res[res$pvalue < pval,]
}




